package Data;
/*
 * LateFeeDAO.java
 *
 * Created on March 7, 2007, 1:21 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

import java.sql.*;
import java.util.*;

/**
 * Database Access Object for the LateFeeBO
 * @author Justin Shaw
 */
public class LateFeeDAO {
    ///////////////////////////////////////
    ///   Singleton pattern
    
    /**
     * The one and only instance of the BO.
     */
    private static LateFeeDAO instance = null;
    
    /** Creates a new instance of LateFee */
    private LateFeeDAO() {
    }
    
    /**
     * Returns the one and only instance of the DAO
     * @return LateFeeDAO instance
     */
    public static synchronized LateFeeDAO getInstance() {
        if (instance == null) {
            instance = new LateFeeDAO();
        }
        return instance;
    }
    
    
    /////////////////////////////////
    ///   CREATE
    
    /**
     * Creates a LateFee
     * @return A new LateFee
     * @throws Exception Problem Creating LateFee
     */
    public LateFeeBO create() throws Exception {
        String id = GUID.generate();
        LateFeeBO LateFee = new LateFeeBO(id);
        Cache c = Cache.getInstance();
        c.put(LateFee.getId(), LateFee);
        return LateFee;
    }
    
    /**
     * Create a new LateFee
     * @return A new LateFee
     * @param id The id for the new LateFee
     */
    public LateFeeBO create(String id){
        LateFeeBO LateFee = new LateFeeBO(id);
        Cache c = Cache.getInstance();
        c.put(LateFee.getId(),LateFee);
        return LateFee;
    }
    
    /**
     * Creates a LateFee from a result Set.
     * @param rs The result set to create the LateFee from
     * @param conn The connection to the DB.
     * @throws Exception Unable to Create Result Set
     * @return A new LateFee
     */
    private LateFeeBO create(ResultSet rs,Connection conn) throws SQLException, DataException{
        LateFeeBO LateFee;
        LateFee = new LateFeeBO(rs.getString("ID"));
        //save to the cache
        Cache.getInstance().put(LateFee.getId(),LateFee);
        LateFee.setAlreadyInDB(true);
        return LateFee;
    }
    
    
    /////////////////////////////////////
    ///   READ
    /**
     * This is the public read statement.  It loads an existing record
     * from the database.
     * @param id The id of the LateFee to read from the DB
     * @return The LateFee Read from the DB
     * @throws Exception Unable to read the LateFee
     */
    public synchronized LateFeeBO read(String id) throws DataException {
        LateFeeBO LateFee;
        Connection conn;
        // check to see if id in the cache
        // if so, return it immediately
        if(Cache.getInstance().containsKey(id)){
            return (LateFeeBO) Cache.getInstance().get(id);
        }
        
        
        try {
            
            // retrieve a database connection from the pool
            conn = ConnectionPool.getInstance().get();
            
            // call read with a connection (the other read method in this class)
            LateFee = LateFeeDAO.getInstance().read(id,conn);
            
            // release the connection
            ConnectionPool.getInstance().release(conn);
            // rollback
            conn.rollback();
        }catch (ConnectionPoolException ex) {
            throw new DataException("Unable to communicate with the Database", ex);
            
        }catch (SQLException e) {
            throw new DataException("Could not retrieve record for id=" + id, e);
        }
        return LateFee;
    }
    /**
     *  This is a package method that is called by the public read (above) or
     *  by another DAO.  Either way we already have a connection to the database
     *  to use.  The user (controller) code never calls this one directly, since
     *  it can't know about Connection objects or SQLExceptions.
     * @param id The id to read from the DB.
     * @param conn The Connection to use to get the BO from the DB.
     * @return The LateFeeBO
     * @throws Exception Unable to read the LateFee
     */
    public LateFeeBO read(String id,Connection conn) throws DataException {
        LateFeeBO LateFee=null;
        // check the cache to see if the customer is in the cache
        // if so, return it immediately
        if(Cache.getInstance().containsKey(id)){
            return (LateFeeBO) Cache.getInstance().get(id);
        }
        
        try{
            PreparedStatement pstmt =conn.prepareStatement("Select * from \"LateFee\" WHERE id=?");
            pstmt.setString(1,id);
            
            ResultSet rs = null;
            rs = pstmt.executeQuery();
            // get a rs from an sql SELECT statement
            // if( rs.next()){convert the rs to the member}
            if(rs.next()){
                LateFee = LateFeeDAO.getInstance().create(rs,conn);
                
                
                
                
                
                // else {handling for no customer with that GUID
            }
        }catch(SQLException e){
            //rollback
            throw new DataException("Could not retrieve record for id=" + id,e);
        }
        //release the connection back to the pool.
        return LateFee;
    }
    
    //////////////////////////////////
    ///   UPDATE
    
    /**
     * This is the public save method.  It is what is called when
     * the user (controller) code wants to save or update an object
     * into the database.
     * @param LateFee The LateFee to save to the DB.
     * @throws Exception Unable to save the BO.
     */
    public synchronized void save(LateFeeBO LateFee) throws DataException {
        
        try {
            // retrieve a database connection from the pool
            Connection conn=ConnectionPool.getInstance().get();
            // call save with a connection (the other save method in this class)
            save(LateFee, conn);
            // release the connection
            ConnectionPool.getInstance().release(conn);
        }catch (SQLException e) {
            // rollback
            throw new DataException("Could not save record for id=" + LateFee.getId(), e);
        }catch (ConnectionPoolException e){
            throw new DataException("Could not retrieve Connection", e);
        }
        
        // release the connection back to the pool
        
    }
    
    
    /**
     *  This is a package method that is called by the public save (above) or
     *  by another DAO.  Either way we already have a connection to the database
     *  to use.  The user (controller) code never calls this one directly, since
     *  it can't know about Connection objects or SQLExceptions.
     *
     *  By having other DAOs call this save method (rather than update or
     *  insert below, each DAO in a chained save (like the CustomerDAO calling
     *  MembershipDAO calling InterestDAO save chain) can independently decide
     *  whether to udpate or insert the BO it's saving.  That's why I made
     *  update and insert private rather than package level -- if you call
     *  them directly from another DAO, this DAO can't decide whether it's
     *  object needs to be inserted or updated.
     * @param LateFee The LateFee to save to the DB.
     * @throws Exception Unable to save the BO.
     * @param conn The connection to use to save the BO.
     */
    synchronized void save(LateFeeBO LateFee, Connection conn) throws SQLException, DataException {
        // check the dirty flag in the object.  if it is dirty,
        // run update or insert
        if (LateFee.isDirty()) {
            if (LateFee.isAlreadyInDB()) {
                update(LateFee, conn);
            }else{
                insert(LateFee, conn);
            }
            // set the dirty flag to false now that we've saved it
            LateFee.setDirty(false);
        }
        
        // call save(bo, conn) on any subobjects (like CustomerDAO to MembershipDAO)
        /*This next line was removed because we don't have all of the code to remediate the circulare saves.
         CustomerDAO.getInstance().save(member.getCustomer(),conn);
         **/
        // touch the cache for the object
    }
    
    /**
     * This method is really a part of save(bo, conn) above.  It could be
     * embedded directly in it, but I've separated it into it's own method
     * to isolate the SQL udpate statement and make it more readable.  But
     * logically, it's really just a part of save.
     * @param LateFee The LateFee to save to the DB.
     * @throws Exception Unable to save the BO.
     * @param conn The connection to use to save the BO.
     */
    private synchronized void update(LateFeeBO LateFee,Connection conn) throws SQLException, DataException {
        // do the update statement
        PreparedStatement pstmt =conn.prepareStatement("UPDATE \"LateFee\" SET \"DaysLate\" = ?" +
                " WHERE \"ID\" = ?");
        pstmt.setInt(1,Integer.valueOf(LateFee.getDaysLate()));
        
        pstmt.executeUpdate();
        
        //Do the membership interests insertion
        
        
        System.out.println("LateFeeUpdateSuccessful");
    }
    /**
     * This method is really a part of save(bo, conn) above.  It could be
     * embedded directly in it, but I've separated it into it's own method
     * to isolate the SQL insert statement and make it more readable.  But
     * logically, it's really just a part of save.
     * @param LateFee The LateFee to save to the DB.
     * @throws Exception Unable to save the BO.
     * @param conn The connection to use to save the BO.
     */
    private synchronized void insert(LateFeeBO LateFee,Connection conn) throws SQLException, DataException {
        // do the insert SQL statement
        PreparedStatement pstmt =conn.prepareStatement("INSERT INTO \"LateFee\" VALUES (?,?,?)");
        pstmt.setString(1,LateFee.getId());
        pstmt.setInt(2,LateFee.getDaysLate());
        
        pstmt.executeUpdate();
        System.out.println("LateFeeInsertSucessful");
        
        //Do the membership interests insertion
        // tell the object that it's now in the db (so we call update next time not insert)
        LateFee.setAlreadyInDB(true);
    }
    
    
    ////////////////////////////////////
    ///   DELETE
    
    // We have decided not to support delete because we would like the LateFees to stay in the database for data archiving.
    
    //////////////////////////////
    ///  SEARCH methods
    
    /**
     * Get a LateFee by the Transaction Line ID.
     * @param transactionLine The transactionLine Associated with the LateFee
     * @param conn The connection to use to save the BO.
     * @throws Exception Unable to save the BO.
     * @return The LateFee that is assigned to that Transaction.  May be null.
     */
    public LateFeeBO getByTransactionLine(String transactionLine, Connection conn) throws  DataException, SQLException {
        LateFeeBO LateFee=null;
        // connection was made previously
        
        // get the Member that has the customerID
        PreparedStatement pstmt =conn.prepareStatement("Select * from \"LateFee\" WHERE \"TransactionLineID\"=? ");
        pstmt.setString(1,transactionLine);
        
        ResultSet rs = null;
        rs = pstmt.executeQuery();
        // get a rs from an sql SELECT statement
        // if( rs.next()){convert the rs to the customer}
        if(rs.next()){
            /*The following code was for creating the member right here in the getByCustomerID method.  This keeps the database from being queried twice for the same information
            //However, a problem arises in that changes to the reading of a member method must be made here as well as in the read(String,conn) method.
            //For this reason, I am going to go straight to the read method with the returned member ID.
            //The ultimate solution would be to create a separate method for creating a member from a result set and call that method from here and the read(String,conn) method.
            member = new MemberBO(rs.getString("id"));
            member.setCreditCardNumber(rs.getString("CreditCard"));
            member.setStartDate(rs.getDate("StartDate"));
            member.setExpireDate(rs.getDate("ExpirationDate"));
            member.setInterests(InterestDAO.getInstance().getByMemberId((member.getId()),conn));
            member.setCustomer(CustomerDAO.getInstance().read(rs.getString("CustomerID")));
            //save to the cache
            Cache.getInstance().put(member.getId(),member);
             */
            LateFee = LateFeeDAO.getInstance().create(rs,conn);
            
            
            //return the member
            return LateFee;
        }else{
            return null;
        }
        
    }
}

